查看原文
其他

贝壳的面经!好像有点简单,但是我却做错了

王中阳 王中阳
2024-08-30

✅文末有面经共享群✅

今天分享粉丝最新投稿的北京贝壳外包面经,将一些问题整理下来感觉不是很难,但是我却做错了,其中感谢网友的指出,那现在你觉得呢?换做是你的话你答得出来吗?

面经整理如下图:

北京贝壳外包

  1. 自我介绍
  2. 项目拷打

Redis 跟 MySQL 以及 Kafka 之间是什么关系?

Redis 是内存数据结构存储系统,用于快速读写和高并发访问。MySQL 是关系型数据库,用于持久化存储结构化数据。Kafka 是分布式消息队列系统,用于处理大规模消息流和数据分发。

在应用中,MySQL 存主要数据,Redis 加速访问,Kafka 传递消息和解耦组件。例如电商系统中,用户信息在 MySQL,购物车数据在 Redis,订单处理消息通过 Kafka 传递。

Kafka partion的概念

在 Kafka 里,Partion 就是把一个主题的数据分成了好几个部分。每个 Partion 都是按顺序存消息的,而且这些消息改不了。同一主题的不同 Partion 能放在不同的地方,这样就能让数据处理得更快更平衡。

Partion 有这些用处:一是存数据,消息按顺序存进去,还给每个消息弄个编号来标明位置。二是能同时处理,多个 Partion 能让不同的人或者组一起处理,效率高。三是不容易出错,如果有地方坏了,也就影响那一部分,整个主题的数据不会丢。

Kafka 偏移量

在 Kafka 里边,偏移量就是个数字,专门用来表明消息在分区里处于啥位置。

打个比方,每个消息在分区里都有个独一份的编号,这就是偏移量。消费者读消息的时候,会记住自己读到哪个编号了。下次再读,就知道从哪儿接着来,不会重复读,也不会把消息给漏了。

比如说,偏移量是 5 ,那就说明这是分区里的第 6 条消息(因为偏移量从 0 开始算)。偏移量能保证 Kafka 里消息处理得有顺序,而且不会丢。

比如说有你在线上发现一条慢SQL,你怎么分析它?

使用 explain ,说明:

首先关注“type”这一项。若显示为“ALL”,意味着进行了全表扫描,这通常不是理想情况,需要寻求改进。若显示为“index”或“range”等,则相对较好。

其次留意“possible_keys”和“key”这两项。“possible_keys”表示可能能够使用的索引,而“key”表示实际使用的索引。倘若“key”这一项为空,意味着未使用索引,此时需要探究原因,例如索引创建是否正确。

再者,“rows”这一项也颇为重要,它代表预计扫描的行数。该数值越大,表明需要处理的数据量越多,可能导致查询速度变慢。

另外,“extra”这一项也值得关注。如果出现“Using filesort”或者“Using temporary”等内容,往往意味着存在问题,需要进行优化

索引的最左前缀原则是什么意思?

感谢网友的指出错误

索引的最左前缀原则是指在使用联合索引时,查询条件必须从索引的最左边列开始,并且尽可能连续匹配索引的列顺序。这意味着查询条件中的列顺序应该与索引列的顺序一致,以便最大限度地利用索引。


测试

测试一

测试二

测试三

示例分析

对于查询条件 "a > 1 and b = 1 and c = 1",能否命中索引取决于索引的创建方式。

  1. 索引 (a, b, c):

  • 这个查询可以命中索引,因为查询条件从最左边的 "a" 开始匹配,并且连续匹配了 "b" 和 "c"。
  • 索引 (b, c, a):

    • 这个查询不能有效利用索引,因为查询条件的顺序与索引列的顺序不匹配。虽然 MySQL 优化器可能会尝试重排这些条件以匹配索引顺序,但 "a > 1" 这个条件可能无法有效利用索引,因为它不是一个等值查询,并且 "a" 在索引中是最后一列。

    查询1和查询3应该都会使用索引,但查询1(按索引顺序)可能会更有效率。查询2可能不会有效地使用索引,因为它没有包含索引的最左列(b)。

    效率问题

    即使查询条件包含了所有索引列,但如果它们的顺序与索引顺序不匹配,索引的使用效率可能会降低。为了获得最佳性能,应该尽可能地遵循最左前缀原则和索引列顺序。

    Redis 常见的数据结构?他们的应用场景?

    1. 字符串(String):可以存储字符串、整数或浮点数。常用于缓存用户信息、计数器、分布式锁等。
    2. 哈希(Hash):适合存储对象,例如存储用户的详细信息。
    3. 列表(List):可以实现队列、栈等数据结构。常用于消息队列、最新文章列表等。
    4. 集合(Set):用于存储不重复的元素,可用于社交关系中的共同好友、抽奖等场景。
    5. 有序集合(Sorted Set):每个元素都有一个分数,可根据分数排序。适用于排行榜、限时活动等。例如,在电商网站中,字符串可以存储商品库存数量,哈希存储商品详情,列表用于存储用户浏览记录,集合用于存储用户收藏的商品,有序集合用于商品销量排行。

    zset 你一般在什么场景下会用?底层的数据结构是什么?

    ZSet(有序集合)通常在以下场景中使用:

    1. 排行榜:例如游戏得分排行榜、商品销量排行榜等,按照分数或销量等进行排序。
    2. 优先级队列:可以根据元素的权重或优先级来处理任务。
    3. 时间序列数据:比如按照时间戳排序的事件记录。Redis中ZSet底层实际有两种数据结构:

    一、压缩列表(ziplist)
    使用条件(以下两个条件需同时满足):

    1. 存储的元素数量较少(通常规定是小于配置参数 zset-max-ziplist-entries ,一般默认是128 ,但可以修改配置)。
    2. 所有元素长度都比较小(规定是小于配置参数 zset-max-ziplist-value ,一般默认是64字节,但可以修改配置)。

    结构特点:

    • 是一种为节省内存而设计的特殊编码结构,将所有的元素和分数紧凑地存储在一起。
    • 元素和它的分数在压缩列表中是交替存储,即第一个元素是成员,第二个元素是分数,第三个元素是成员,第四个元素是分数,以此类推。
    • 在redis的源代码中,压缩列表(ziplist)的结构并没有直接定义为一个C结构体,而是通过一系列的宏和函数来操作一段连续的内存1。其结构大致包含以下部分:
      • zlbytes:一个4字节的整数,表示整个压缩列表占用的字节数量,包括它自身的大小。zltail :一个4字节的整数,表示压缩列表中最后一个元素的偏移量(相对于整个压缩列表的起始地址)。
      • zllen:一个2字节的整数,表示压缩列表中的元素数量。如果元素数量超过65535,那么这个值就会被设定为65535,需要遍历整个压缩列表才能获取到实际的元素数量。
      • entry:压缩列表中的元素,每个元素都由一个或多个字节组成。每个元素的第一个字节(又称为 "entry header")用于表示这个元素的长度以及编码方式。
      • zlend:一个字节,值为255,表示压缩列表的结束。


    二、跳跃表(skiplist)

    使用条件(只要有一个不满足上述ziplist条件就会切换到跳跃表):当ZSet存储的元素数量较多,或者元素的字符串长度较长时。

    结构特点:

    在Redis的源代码中,跳跃表的结构定义如下:

    • obj:成员对象。
    • score:分值(用于排序)。
    • backward:后退指针,指向位于当前节点的前一个节点(程序从表尾向表头遍历时使用,每个节点只有一个后退指针 )。
    • level:是一个可变长数组结构(C99标准引入),里面包含:
    • forward:前进指针,用于访问位于表尾方向的其他节点。
    • span:这个层跨越的节点数量(记录前进指针所指向节点和当前节点的距离跨度越大、距离越远 )。
    • header:指向跳跃表的表头节点,通过这个指针程序定位表头节点的时间复杂度是O(1)。
    • tail :指向跳跃表的表尾节点,通过这个指针程序定位表尾节点的时间复杂度为O(1)。
    • length:记录跳跃表的长度,就是跳跃表目前包含节点的数量(表头节点不算在内)。
    • level:记录在目前的跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。
    • 有一个 zskiplist 结构体表示整个跳跃表
    • 跳跃表节点 zskiplistnode

    跳跃表通过维护多级索引来实现快速查找和高效的插入、删除、更新元素等操作。它的时间复杂度平均为O(log n) ,性能比较高。

    在Redis中ZSet元素的存储和访问等操作过程中:

    当使用跳跃表时,元素的成员和分值等信息存储在跳跃表节点中,同时还有一个哈希表(在整个ZSet结构层面,不是跳跃表结构内部)用于快速通过成员查找其分值等操作。当使用压缩列表时,直接在压缩列表中操作元素和分值等数据。

    JWT 是什么东西?

    JWT(JSON Web Token)是一种用于在网络应用环境中进行安全信息传递的开放标准。

    它由三部分组成:

    头部(Header),通常包含令牌的类型和使用的加密算法;

    载荷(Payload),包含声明,比如用户身份信息、权限等;

    签名(Signature),用于验证消息的完整性和真实性,防止被篡改。

    JWT 的主要优点是无状态,服务器不需要保存会话信息,减轻了服务器的存储负担它常用于身份验证和授权,比如在单点登录、微服务架构等场景中。用户在登录成功后获取 JWT 令牌,后续的请求携带该令牌,服务器通过验证令牌的有效性来确认用户身份和权限。

    协程了解吗?和线程对比

    协程和线程是在编程中常见的概念,它们有一些明显的区别。

    线程是操作系统层面的概念,由操作系统进行调度和管理。多个线程可以在同一进程中并发执行,它们共享进程的资源。线程的切换通常由操作系统控制,开销相对较大。协程则是在用户态实现的轻量级线程。协程的切换由程序自身控制,不需要操作系统的干预,因此协程切换的开销通常比线程小得多。协程更适用于协作式的任务调度,多个协程可以在同一个线程中运行,通过主动让出控制权来实现任务的切换。

    在资源消耗方面,线程由于需要操作系统进行管理和调度,会占用较多的系统资源,如内存等。而协程相对来说资源消耗较少。

    在编程模型上,线程的并发模型相对较为复杂,需要处理线程安全、同步等问题。协程的编程模型通常更简单直观,更容易理解和实现复杂的逻辑控制。

    一道算法题

    找到具有最大和的连续子数组,并返回该子数组

    编程题

    用两个协程、两个channel循环打印1, 2

    package main

    import (
       "fmt"
       "sync"
    )

    func Print1(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
       defer wg.Done()
       for {
          <-ch1
          fmt.Print("1")
          ch2 <- true
       }
    }

    func Print2(ch1 chan bool, ch2 chan bool, wg *sync.WaitGroup) {
       defer wg.Done()
       for {
          <-ch2
          fmt.Print("2")
          ch1 <- true
       }
    }

    func main() {
       var wg sync.WaitGroup
       ch1 := make(chan bool)
       ch2 := make(chan bool)

       wg.Add(2)
       go Print1(ch1, ch2, &wg)
       go Print2(ch1, ch2, &wg)

       ch1 <- true
       wg.Wait()
    }

    早日上岸!

    我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

    没准能让你能刷到自己意向公司的最新面试题呢。

    感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。

    点击下方文章,看看他们是怎么找到好工作的!

    这些朋友赢麻了!

    我们又出成绩啦!大厂Offer集锦!遥遥领先!


    继续滑动看下一个
    王中阳
    向上滑动看下一个

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存